/*
 *
 * Copyright (c) 2013 - 2020 Lijun Liao
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package cn.jiedanba.cacert.common.ca.generator;

import java.math.BigInteger;
import java.security.SecureRandom;

import cn.jiedanba.cacert.common.ca.PkiUtil;

/**
 * 序列号生成器
 * 
 * @author dell
 *
 */
public class RandomSerialNumberGenerator {

	private static RandomSerialNumberGenerator instance;

	private final SecureRandom random;

	private RandomSerialNumberGenerator() {
		this.random = new SecureRandom();
	}

	/**
	 * Generate the next serial number.
	 * 
	 * @param byteLen
	 *            byte length of the serial number.
	 * @return the serial number.
	 */
	public BigInteger nextSerialNumber(int byteLen) {
		final byte[] rndBytes = new byte[byteLen];
		final int minWeight = byteLen * 2;

		while (true) {
			random.nextBytes(rndBytes);
			// set the first bit to 0.
			rndBytes[0] &= 0x7F;

			// check NAF weight
			BigInteger bi = new BigInteger(rndBytes);

			BigInteger threeBi = bi.shiftLeft(1).add(bi);
			BigInteger diff = threeBi.xor(bi);
			int nafWeight = diff.bitCount();
			if (nafWeight >= minWeight) {
				return bi;
			}
		}

	} // method nextSerialNumber

	public static synchronized RandomSerialNumberGenerator getInstance() {
		if (instance == null) {
			instance = new RandomSerialNumberGenerator();
		}
		return instance;
	} // method RandomSerialNumberGenerator

	public static void main(String[] args) {
		BigInteger sn = RandomSerialNumberGenerator.getInstance().nextSerialNumber(10);
		System.out.println(PkiUtil.serialNumberConvertString(sn));
	}

}
